package com.it.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.it.gmall.common.result.Result;
import com.it.gmall.common.result.ResultCodeEnum;
import com.it.gmall.gateway.constant.RedisConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;

@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private RedisTemplate redisTemplate;

    // 注入路径 同步跳转中必须要求登陆的路径
    @Value("gateway.urls")
    private String[] urls;

    public static final String urlPrefix = "http://passport.gmall.com/login.html?originUrl=";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取response路径
        /*
        ServerHttpResponse 网关 响应式的 netty
        HttpServletRequest 分布式的 非响应式的 springmvc
        * */
        ServerHttpResponse response = exchange.getResponse();
        // 获取页面路径
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        System.out.println("path:"+path);
        // 获取完整路径
        String originUrl = request.getURI().toString();
        System.out.println("originUrl:"+originUrl);

        /*1.判断是否是内部资源 包含inner 内部资源-没有访问权限*/
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 匹配路径
        if(antPathMatcher.match("/**/inner/**",path)){
            // 说明是内部资源 没有访问权限
            return getVoidMono(response,ResultCodeEnum.PERMISSION);
        }

        /*2.判断用户是否登录*/
        /*
        * 同步 跳转页面 重定向 存储在cookie中
        * 异步 弹出层   存储在请求头中 ajax不携带cookie信息*/
        // 获取token 获取用户id
        String userId = this.getUserString(request);

        /*3.登录*/
        /*同步登录
        * 异步登录*/
        if(antPathMatcher.match("/**/auth/**",path) && StringUtils.isEmpty(userId)){
            // 页面要求带auth的是必须登录的页面 异步跳转 且要求用户id必须存在即用户必须登录
            // 返回 208 未登录
            return this.getVoidMono(response,ResultCodeEnum.LOGIN_AUTH);
        }
        for (String u : urls) {
            if(path.indexOf(u)!=-1 && StringUtils.isEmpty(userId)){
                // 页面要求必须登录 且 用户没有登陆的时候
                // 重定向到请登录页面
                // 'http://passport.gmall.com/login.html?originUrl='+ encodeURIComponent(window.location.href);
                // 响应头 响应体 响应码 相应行
                response.setStatusCode(HttpStatus.SEE_OTHER); // 响应码 303
                try {
                    // 响应头
                    response.getHeaders().add(HttpHeaders.LOCATION,urlPrefix+ URLEncoder.encode(originUrl,"utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                // 开始响应
                return response.setComplete();
            }
        }

        // 将用户的id放在网关的request中
        // 真实的用户id
        // return new DefaultServerHttpRequestBuilder(this);
        if(!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId);
            System.out.println("userId:"+userId);
        }
        // 临时的用户id
        String userTempString = getUserTempString(request);
        if(!StringUtils.isEmpty(userTempString)){
            request.mutate().header("userTempId",userTempString);
            System.out.println("userTempId:"+userTempString);
        }

        // 放行
        return chain.filter(exchange);
    }

    // 创建临时的用户id
    private String getUserTempString(ServerHttpRequest request) {
        // 先获取请求头
        String userTempId = request.getHeaders().getFirst("userTempId");
        if(StringUtils.isEmpty(userTempId)){// 请求头没有 取cookie中的值
            HttpCookie cookie = request.getCookies().getFirst("userTempId");
            if(null!=cookie){
                userTempId=cookie.getValue();
            }
        }
        return userTempId;
    }

    // 对返回json数据的封装
    private Mono<Void> getVoidMono(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        String resultStr = JSONObject.toJSONString(Result.build("", resultCodeEnum));
        DataBuffer wrap = response.bufferFactory().wrap(resultStr.getBytes());
        // 防止响应乱码 (content-type:application/json;charset=UTF-8;)
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(wrap));
    }

    // 获取用户id
    private String getUserString(ServerHttpRequest request) {
        // 先获取请求头
        String token = request.getHeaders().getFirst("token");
        if(StringUtils.isEmpty(token)){// 请求头没有 取cookie中的值
            HttpCookie cookie = request.getCookies().getFirst("token");
            if(null!=cookie){
                token=cookie.getValue();
            }
        }
        if(!StringUtils.isEmpty(token)){// 如果token不为空 从缓存中获取用户id
            if(redisTemplate.hasKey(RedisConst.USER_LOGIN_KEY_PREFIX + token)){ // 如果缓存中有这个字段
                String userId = (String) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + token);
                return userId;
            }

        }
        return null;
    }

    @Override
    public int getOrder() {
        /*网关自带了九个过滤的 数值越小越小执行 */
        // Integer.MIN_VALUE  最小
        // Integer.MAX_VALUE  最大
        return 0;
    }
}
